接下來時間真的很緊,也顧不上結構了,只能就目前想到的功能,先以直覺的方式編寫了,如果講不太清楚還多多包涵!昨天的一定要先看唷,今天是接著作下去的
玩家從進到網頁開始,一直到遊戲結束,總共會經歷四個場景:
調整一下昨天設計的:「點擊畫面任一處,重新長出一顆新的樹」取名為MakeTree函式,方便我們在離開讀取畫面後,可以移除這個功能(事件):
startScreen.addEventListener('click', MakeTree);
function MakeTree(){
treeGrowth.Restore();
myTree = new Tree(WIDTH/2, 0.8 * HEIGHT, HEIGHT/6, 90, maxTimes);
}
樹的生長1-1和讀取進度條1-2昨天都完成了,這邊繼續添加選單淡入1-3的功能,是在按下開始鍵後始選單1.5秒(90幀)內淡入,覆蓋整個畫面,初始設定:
let opacity = new Trail(0, 0, false);
按下Start按鈕時,移除MakeTree函式,接著設定轉場效果:
Start.addEventListener("click", function(event){
// 設定
event.stopPropagation();
// 執行
startScreen.removeEventListener('click', MakeTree);
Start.style.display = "none";
opacity.NewTarget(1, 0, 90);
});
由於昨天有設置整個畫面的click作為讓樹重新長出來的互動,如果點了Start按鈕也會觸發則顯得奇怪,因此在點擊事件中做阻擋冒泡的設定,這樣點擊時,這樣就不算點擊到整個startScreen
然後在昨天設計的動畫框架中,添加1-3選單淡入,設定header的opacity:
function LoadingScreen(){
try{
Resize("#game-box", canvas, context, '#000');
clear(context);
// 1. 讓樹長出來
treeGrowth.NextFrame(1, -1, 3);
myTree.Transform();
myTree.Draw();
// 2. 讀取進度條
if(loading.timer > 0){
loading.NextFrame(1, 0, 2);
let percent = Math.floor(loading.pointX * 100);
Start.style.width = minWidth + percent + "px";
Start.textContent = percent + "%";
}
else if(Start.disabled == true){
Start.textContent = "Start";
Start.disabled = false;
}
// 3. 選單淡入
if(opacity.timer > 0){
opacity.NextFrame(1, 0, 2);
let header = document.getElementsByTagName("header")[0];
header.style.opacity = opacity.pointX;
}
}catch(e){
console.log(e);
return;
}
loadingAnime = requestAnimationFrame(LoadingScreen);
}
NextFrame內部會自行判斷timer是否已歸零(動畫結束),這邊的if判斷式在於限制dom的操作在90次(幀數frames=90),為了應該會嘗試把它整合到NextFrame的原型方法中,今天沒這個時間啦~~
2-1是銜接1-3,讓選單淡入後,等待玩家點擊Play,就讓選單淡出,此過程從場景1換到場景2,因此在這邊:
Play.addEventListener("click", function(event){
// 讓玩家發現,剛剛開場的那顆樹,已經默默地成長為參天大樹
myTree = new Tree(WIDTH/2, 0.8 * HEIGHT, HEIGHT/2, 90, maxTimes);
// 設定淡出和運鏡
opacity.NewTarget(0, 0, 90);
camera.NewTarget(0.1, 0.3, 120); // 第一次運鏡到左下角
// 切換場景 1>2
cancelAnimationFrame(loadingAnime);
openingAnime = requestAnimationFrame(OpeningScreen);
});
直接創建了一顆新的樹,嚴謹一點的作法可以在使用原型方法Transform,並在其中修改樹根長度的參數,這樣可以保證是同一棵樹,且形狀相同,不過今天沒時間去修改了><
運鏡起始點:
let camera = new Trail(0, 0, false);
運鏡這邊用偷吃步的作法,把四個運鏡接起來,邏輯就是每次timer歸零,就會進入載入下一次的運鏡,然後把幀數當作編號,依序用120、121、122、123表示四種運鏡,因此當編號120的運鏡結束(timer歸零)時,就會設定新的路徑編號為121,以此類推,當然這樣寫有點佔空間,未來可以再多花時間去思考能怎麼樣包成更簡單的函式:
function OpeningScreen(){
try{
Resize("#game-box", canvas, context, undefined);
clear(context);
// 1. 選單淡出
if(opacity.timer > 0){
opacity.NextFrame(1, 0, 2);
let header = document.getElementsByTagName("header")[0];
header.style.opacity = opacity.pointX;
}
// 2. 運鏡
let x = camera.pointX * WIDTH;
let y = camera.pointY * HEIGHT * 1;
context.translate(x, y);
myTree.Transform();
myTree.Draw(context);
context.translate(-x, -y);
if(camera.timer > 0){
if(camera.period == 120){
camera.NextFrame(1, 1.5, 0);
}
else if(camera.period == 121){
camera.NextFrame(1, 0, 1);
}
else if(camera.period == 122){
camera.NextFrame(1, 1.5, 0);
}
else if(camera.period == 123){
camera.NextFrame(1, 0, 1.5);
}
}
else if(camera.period == 120){
camera.NewTarget(0, 1, 121); // 第二次運鏡到正上方
}
else if(camera.period == 121){
camera.NewTarget(-0.3, 0.2, 122); // 第三次運鏡到右下角
}
else if(camera.period == 122){
camera.NewTarget(0, 0.6, 123); // 第四次運鏡到中間
}
else{
// 運鏡結束,直接進入第三個場景:遊戲進行畫面
GamingAnime = requestAnimationFrame(GamingScreen);
// 直接在此處中斷即可結束該開場動畫
return;
}
}catch(e){
console.log(e);
return;
}
openingAnime = requestAnimationFrame(OpeningScreen);
}
以camera的座標去換算,運鏡分別設定四個點,讓鏡頭順時針繞一圈,呈螺旋狀(底部>左下>正上方>右下>中間)
做到這邊我發現一個致命問題,接下來的第三個場景需要一個靜態畫布+動態畫布,而我原先設計的動畫框架,沒有考慮到使用兩個以上的畫布的情況,因此有點難以擴充,搞了一下子發現事情不太對勁,要重新編修整個框架,包含Resize動態調整寬高的方式,這部份明天繼續!
https://jerry-the-potato.github.io/Chapter5/
運鏡的部分稍微有點粗糙,主要是因為這四個運鏡都是直線,如果之後有把第四章附錄的貝茲曲線繼續做完,就可以應用在這上面,便再也不用擔心運鏡看起來卡卡的囉!